// +FHDR------------------------------------------------------------
//                 Copyright (c) 2024 NOVAUTO.
//                       ALL RIGHTS RESERVED
// -----------------------------------------------------------------
// Filename      : sync_fifo.v
// Author        : ICer
// Created On    : 2024-01-02 15:30
// Last Modified : 2024-01-02 19:35 by ICer
// -----------------------------------------------------------------
// Description:
//
//
// -FHDR------------------------------------------------------------


module sync_fifo #(
    //parameter
    parameter WIDTH = 8,
    parameter DEPTH = 8
)( /*AUTOARG*/
   // Outputs
   wfull, almost_wfull, rdata, rempty, almost_rempty, rvld,
   // Inputs
   clk, rst_n, afull_th, aempty_th, winc, wdata, rinc
   );

// ----------------------------------------------------------------
// Interface declare
// ----------------------------------------------------------------
input             clk;
input             rst_n;

input [WIDTH -1:0]afull_th;
input [WIDTH -1:0]aempty_th;

input             winc;
input [WIDTH -1:0]wdata;
output            wfull;
output            almost_wfull;

input             rinc;
output[WIDTH -1:0]rdata;
output            rempty;
output            almost_rempty;
output            rvld;

// ----------------------------------------------------------------
// Wire declare
// ----------------------------------------------------------------
localparam ADDR_WD    = $clog2(DEPTH);
localparam ADDR_WD_EX = ADDR_WD + 1;

wire wenc = winc && (!wfull);//to ram
wire renc = rinc && (!rempty);//ro ram

// ----------------------------------------------------------------
// AUTO declare
// ----------------------------------------------------------------
/*AUTOOUTPUT*/
/*AUTOINPUT*/
/*AUTOWIRE*/

// ----------------------------------------------------------------
// fifo cnt
// ----------------------------------------------------------------
reg [ADDR_WD_EX -1:0]fifo_cnt;

always @(posedge clk or negedge rst_n) begin
  if(!rst_n) begin
    fifo_cnt <= {ADDR_WD_EX{1'b0}};
  end 
  else if(wenc ^ renc) begin
    if(wenc) fifo_cnt <= fifo_cnt + 1'b1;
    else     fifo_cnt <= fifo_cnt - 1'b1;
  end
end

// ----------------------------------------------------------------
// w ptr
// ----------------------------------------------------------------
reg [ADDR_WD -1:0]waddr;

always @(posedge clk or negedge rst_n) begin
  if(!rst_n) begin
    waddr <= {ADDR_WD{1'b0}};
  end 
  else if(wenc) begin
    if(waddr >= DEPTH - 1'b1) waddr <= {ADDR_WD{1'b0}};
    else                      waddr <= waddr + 1'b1;
  end
end

// ----------------------------------------------------------------
// r ptr
// ----------------------------------------------------------------
reg [ADDR_WD -1:0]raddr;

always @(posedge clk or negedge rst_n) begin
  if(!rst_n) begin
    raddr <= {ADDR_WD{1'b0}};
  end 
  else if(renc) begin
    if(raddr >= DEPTH - 1'b1) raddr <= {ADDR_WD{1'b0}};
    else                      raddr <= raddr + 1'b1;
  end
end

// ----------------------------------------------------------------
// ram inst
// ----------------------------------------------------------------
/*dual_port_ram AUTO_TEMPLATE(
.wclk(clk),
.rclk(clk),
);
*/
dual_port_ram #(/*AUTOINSTPARAM*/
                // Parameters
                .DEPTH                  (DEPTH),
                .WIDTH                  (WIDTH))
u_ram(/*AUTOINST*/
      // Outputs
      .rdata                            (rdata[WIDTH-1:0]),
      // Inputs
      .wclk                             (clk),                   // Templated
      .wenc                             (wenc),
      .waddr                            (waddr[$clog2(DEPTH)-1:0]),
      .wdata                            (wdata[WIDTH-1:0]),
      .rclk                             (clk),                   // Templated
      .renc                             (renc),
      .raddr                            (raddr[$clog2(DEPTH)-1:0]));

// ----------------------------------------------------------------
// output logic
// ----------------------------------------------------------------
reg renc_ff;

assign wfull         = (fifo_cnt >= DEPTH);
assign almost_wfull  = (fifo_cnt >= afull_th);
assign rempty        = (fifo_cnt == {ADDR_WD_EX{1'b0}});
assign almost_rempty = (fifo_cnt <= aempty_th);
assign rvld          = renc_ff;

always @(posedge clk or negedge rst_n) begin
  if(!rst_n) begin
    renc_ff <= 1'b0;
  end 
  else begin
    renc_ff <= renc;
  end
end

endmodule
// Local Variables:
// verilog-auto-inst-param-value:t
// verilog-library-directories:(".")
// verilog-library-extensions:(".v")
// End:

